package com.ds.infrastructure.hyperspace.container.flow.handler;

import com.ds.infrastructure.hyperspace.common.Container;
import com.ds.infrastructure.hyperspace.common.Operator;
import com.ds.infrastructure.hyperspace.common.config.TargetNodeConfig;
import com.ds.infrastructure.hyperspace.common.flow.handler.PreFlowHandler;
import com.ds.infrastructure.hyperspace.container.flow.FlowHandlerInitException;
import com.ds.infrastructure.hyperspace.container.flow.context.HyperspaceDuplexFlowContext;
import com.ds.infrastructure.hyperspace.container.internal.gzs.GzsService;
import com.ds.infrastructure.hyperspace.container.internal.operator.OperatorDefinition;
import com.ds.infrastructure.zonesdk.Zone;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

import static com.ds.infrastructure.hyperspace.common.StateEnum.REQUEST_BLOCKED;

/**
 * @author  Weishaoying
 */
@Slf4j
public abstract class AbstractPreFlowHandler implements PreFlowHandler<HyperspaceDuplexFlowContext> {

    private static final int DEFAULT_OPERATOR_CAPACITY = 3;

    protected List<OperatorDefinition> parseOperatorDefinition(Container container, String[] handlerParams, int fromIndex) {
        if (handlerParams == null || handlerParams.length <= fromIndex) {
            return null;
        }
        List<OperatorDefinition> operatorDefinitionList = new ArrayList<>(DEFAULT_OPERATOR_CAPACITY);
        int i = fromIndex;
        while(i < handlerParams.length) {
            String operatorName = handlerParams[i];
            Operator operator = container.getOperator(operatorName);
            if (operator == null) {
                log.error("Input operator not found, operator name [{}]", operatorName);
                throw new FlowHandlerInitException("Operator [" + operatorName + "]not found!");
            }
            String[] operatorArgs = null;
            int argsLength = operator.getOperatorArgsLength();
            if (handlerParams.length <= i + argsLength) {
                log.error("Input operator args invalid, operator name [{}].", operatorName);
                throw new FlowHandlerInitException("Operator [" + operatorName + "] args invalid!");
            }
            if (argsLength > 0) {
                operatorArgs = new String[argsLength];
                System.arraycopy(handlerParams, i + 1, operatorArgs, 0, argsLength);
            }
            operatorDefinitionList.add(new OperatorDefinition(operatorName, operatorArgs, operator));
            i += argsLength + 1;
        }
        return operatorDefinitionList;
    }

    protected String extractAndComputeInput(HyperspaceDuplexFlowContext duplexFlowContext,
            String inputName, List<OperatorDefinition> operatorDefinitionList) {
        String inputValue;
        if (URI_CONFIG_PARAM_NAME.equals(inputName)) {
            inputValue = duplexFlowContext.getUri();
        } else if (IP_CONFIG_PARAM_NAME.equals(inputName)) {
            inputValue = duplexFlowContext.getClientIp();
        } else {
            inputValue = duplexFlowContext.getEntryProtocolAdapter()
                    .getEntryHeader(duplexFlowContext, inputName );
        }
        if (!CollectionUtils.isEmpty(operatorDefinitionList)) {
            for (OperatorDefinition operatorDefinition : operatorDefinitionList) {
                inputValue = operatorDefinition.compute(inputValue);
                if (inputValue == null) {
                    return null;
                }
            }
        }
        return inputValue;
    }

    protected Zone findZone(HyperspaceDuplexFlowContext duplexFlowContext,
            String shardingType, String shardingKey) {
        if (shardingKey == null) {
            return null;
        }
        GzsService gzsService = duplexFlowContext.getNettyContainer().getGzsService();
        if (gzsService == null) {
            log.warn("Find zone operation ignore, gzs service not init.");
            return null;
        }
        return gzsService.getZone(shardingType, shardingKey);
    }

    protected void assertZoneIsActive(String defaultZone, Zone zone,
            HyperspaceDuplexFlowContext duplexFlowContext,
            String shardingType, String shardingKey) {
        if (zone == null && defaultZone != null) {
            duplexFlowContext.setZoneId(defaultZone.toLowerCase());
        } else if (zone != null && Zone.ZoneStatus.ACTIVE == zone.getStatus()) {
            duplexFlowContext.setShardingInfo(shardingType, shardingKey, zone);
        } else if (TargetNodeConfig.DEFAULT_ZONE.equals(duplexFlowContext.getZoneId())) {
            String interruptMessage = new StringBuilder()
                    .append("Gzs zone info [")
                    .append(zone == null ? "null" : zone.toString())
                    .append("], sharding type [")
                    .append(shardingType)
                    .append("], sharding key [")
                    .append(shardingKey)
                    .append("]").toString();
            duplexFlowContext.getEntryFlowOperation().interruptAndCloseContext(
                    duplexFlowContext, REQUEST_BLOCKED, interruptMessage);
        }
    }

}
